using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.PowerShell.CmdletManagement
{
    public class PsSpecCmdletParameterDetails : IComparable
    {
        private PsSpecCmdletParameter parameter;
        private List<PsSpecCmdletParameterSetEntry> parameterSetEntries;
        private List<PsSpecCmdletParameterSetEntry> removedParameterSetEntries;

        public PsSpecCmdletParameterDetails(PsSpecCmdletParameter parameter, List<PsSpecCmdletParameterSetEntry> parameterSetEntries)
        {
            this.parameter = parameter;
            this.parameterSetEntries = parameterSetEntries;
            this.removedParameterSetEntries = new List<PsSpecCmdletParameterSetEntry>();
        }

        public static class Constants
        {
            public static string VariesByParameterSet = "<Varies by parameter set>";
        }

        public int Id
        {
            get { return parameter.Id; }
            set { parameter.Id = value; }
        }

        public string ProjectName
        {
            get { return parameter.ProjectName; }
            set { parameter.ProjectName = value; }
        }

        public string CmdletName
        {
            get { return parameter.CmdletName; }
            set { parameter.CmdletName = value; }
        }

        public string Name
        {
            get { return parameter.Name; }
            set { parameter.Name = value; }
        }

        public string Type
        {
            get { return parameter.Type; }
            set { parameter.Type = value; }
        }

        public string Description
        {
            get { return parameter.Description; }
            set { parameter.Description = value; }
        }

        public string HelpDescription
        {
            get { return parameter.HelpDescription; }
            set { parameter.HelpDescription = value; }
        }

        public string DefaultValue
        {
            get { return parameter.DefaultValue; }
            set { parameter.DefaultValue = value; }
        }

        public string[] Alias
        {
            get { return parameter.Alias; }
            set { parameter.Alias = value; }
        }

        public bool AllowGlobbing
        {
            get { return parameter.AllowGlobbing; }
            set { parameter.AllowGlobbing = value; }
        }

        public bool AllowMultiple
        {
            get { return parameter.AllowMultiple; }
            set { parameter.AllowMultiple = value; }
        }

        public bool AllowEmptyCollection
        {
            get { return parameter.AllowEmptyCollection; }
            set { parameter.AllowEmptyCollection = value; }
        }

        public bool AllowEmptyString
        {
            get { return parameter.AllowEmptyString; }
            set { parameter.AllowEmptyString = value; }
        }

        public bool AllowNull
        {
            get { return parameter.AllowNull; }
            set { parameter.AllowNull = value; }
        }

        public int[] ValidateCount
        {
            get { return parameter.ValidateCount; }
            set { parameter.ValidateCount = value; }
        }

        public bool ValidateNotNull
        {
            get { return parameter.ValidateNotNull; }
            set { parameter.ValidateNotNull = value; }
        }

        public bool ValidateNotNullOrEmpty
        {
            get { return parameter.ValidateNotNullOrEmpty; }
            set { parameter.ValidateNotNullOrEmpty = value; }
        }

        public int[] ValidateRange
        {
            get { return parameter.ValidateRange; }
            set { parameter.ValidateRange = value; }
        }

        public string[] ValidateSet
        {
            get { return parameter.ValidateSet; }
            set { parameter.ValidateSet = value; }
        }

        public int[] ValidateLength
        {
            get { return parameter.ValidateLength; }
            set { parameter.ValidateLength = value; }
        }

        public string ValidatePattern
        {
            get { return parameter.ValidatePattern; }
            set { parameter.ValidatePattern = value; }
        }

        public bool IsDynamic
        {
            get { return parameter.IsDynamic; }
            set { parameter.IsDynamic = value; }
        }

        public string Notes
        {
            get { return parameter.Notes; }
            set { parameter.Notes = value; }
        }

        public string Custom1
        {
            get { return parameter.Custom1; }
            set { parameter.Custom1 = value; }
        }

        public string Custom2
        {
            get { return parameter.Custom2; }
            set { parameter.Custom2 = value; }
        }

        public string Custom3
        {
            get { return parameter.Custom3; }
            set { parameter.Custom3 = value; }
        }

        public string Custom4
        {
            get { return parameter.Custom4; }
            set { parameter.Custom4 = value; }
        }

        public String Position
        {
            get
            {
                String returnPosition = null;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        if (returnPosition == null)
                        {
                            returnPosition = parameterSetEntry.Position.ToString();
                        }
                        else if (parameterSetEntry.Position.ToString() != returnPosition)
                        {
                            returnPosition = Constants.VariesByParameterSet;
                        }
                    }
                }

                return returnPosition;
            }
            set
            {
                int? appliedPosition = null;
                if (!String.IsNullOrEmpty(value))
                    appliedPosition = Int32.Parse(value);

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        parameterSetEntry.Position = appliedPosition;
                    }
                }
            }
        }

        public bool? Mandatory
        {
            get
            {
                bool someTrue = false;
                bool someFalse = false;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        someTrue |= (parameterSetEntry.Mandatory == true);
                        someFalse |= (parameterSetEntry.Mandatory == false);
                    }
                }
                if (someTrue && someFalse) return null;
                else if (someTrue) return true;
                else return false;
            }
            set
            {
                if (value != null)
                {
                    foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                    {
                        if (parameterSetEntry.ParameterId == this.Id)
                        {
                            parameterSetEntry.Mandatory = (bool)value;
                        }
                    }
                }
            }
        }

        public bool? ValueFromPipeline
        {
            get
            {
                bool someTrue = false;
                bool someFalse = false;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        someTrue |= (parameterSetEntry.ValueFromPipeline == true);
                        someFalse |= (parameterSetEntry.ValueFromPipeline == false);
                    }
                }
                if (someTrue && someFalse) return null;
                else if (someTrue) return true;
                else return false;
            }
            set
            {
                if (value != null)
                {
                    foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                    {
                        if (parameterSetEntry.ParameterId == this.Id)
                        {
                            parameterSetEntry.ValueFromPipeline = (bool)value;
                        }
                    }
                }
            }
        }

        public bool? ValueFromPipelineByPropertyName
        {
            get
            {
                bool someTrue = false;
                bool someFalse = false;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        someTrue |= (parameterSetEntry.ValueFromPipelineByPropertyName == true);
                        someFalse |= (parameterSetEntry.ValueFromPipelineByPropertyName == false);
                    }
                }
                if (someTrue && someFalse) return null;
                else if (someTrue) return true;
                else return false;
            }
            set
            {
                if (value != null)
                {
                    foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                    {
                        if (parameterSetEntry.ParameterId == this.Id)
                        {
                            parameterSetEntry.ValueFromPipelineByPropertyName = (bool)value;
                        }
                    }
                }
            }
        }

        public bool? ValueFromRemainingArguments
        {
            get
            {
                bool someTrue = false;
                bool someFalse = false;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        someTrue |= (parameterSetEntry.ValueFromRemainingArguments == true);
                        someFalse |= (parameterSetEntry.ValueFromRemainingArguments == false);
                    }
                }
                if (someTrue && someFalse) return null;
                else if (someTrue) return true;
                else return false;
            }
            set
            {
                if (value != null)
                {
                    foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                    {
                        if (parameterSetEntry.ParameterId == this.Id)
                        {
                            parameterSetEntry.ValueFromRemainingArguments = (bool)value;
                        }
                    }
                }
            }
        }

        public String HelpMessage
        {
            get
            {
                String returnMessage = null;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        if (returnMessage == null)
                        {
                            returnMessage = parameterSetEntry.HelpMessage;
                        }
                        else if (parameterSetEntry.HelpMessage != returnMessage)
                        {
                            returnMessage = Constants.VariesByParameterSet;
                        }
                    }
                }

                return returnMessage;
            }
            set
            {
                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        parameterSetEntry.HelpMessage = value;
                    }
                }
            }
        }

        public String HelpMessageBaseName
        {
            get
            {
                String returnMessage = null;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        if (returnMessage == null)
                        {
                            returnMessage = parameterSetEntry.HelpMessageBaseName;
                        }
                        else if (parameterSetEntry.HelpMessageBaseName != returnMessage)
                        {
                            returnMessage = Constants.VariesByParameterSet;
                        }
                    }
                }

                return returnMessage;
            }
            set
            {
                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        parameterSetEntry.HelpMessageBaseName = value;
                    }
                }
            }
        }

        public String HelpMessageResourceId
        {
            get
            {
                String returnMessage = null;

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        if (returnMessage == null)
                        {
                            returnMessage = parameterSetEntry.HelpMessageResourceId;
                        }
                        else if (parameterSetEntry.HelpMessageResourceId != returnMessage)
                        {
                            returnMessage = Constants.VariesByParameterSet;
                        }
                    }
                }

                return returnMessage;
            }
            set
            {
                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        parameterSetEntry.HelpMessageResourceId = value;
                    }
                }
            }
        }

        public List<string> ParameterSets
        {
            get
            {
                List<string> parameterSetNames = new List<string>();

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        parameterSetNames.Add(parameterSetEntry.ParameterSetName);
                    }
                }

                return parameterSetNames;
            }
            set
            {
                List<string> currentParameterSetNames = new List<string>();

                // First check for removals
                List<PsSpecCmdletParameterSetEntry> entriesToRemove = new List<PsSpecCmdletParameterSetEntry>();                           
                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    if (parameterSetEntry.ParameterId == this.Id)
                    {
                        // Add the current parameter set name to the list of
                        // parameter set names for this parameter
                        if (!currentParameterSetNames.Contains(parameterSetEntry.ParameterSetName))
                        {
                            currentParameterSetNames.Add(parameterSetEntry.ParameterSetName);                            
                        }

                        // Mark an item for removal if it's not in the new sets
                        if (!value.Contains(parameterSetEntry.ParameterSetName))
                        {
                            entriesToRemove.Add(parameterSetEntry);
                        }
                    }
                }

                // Now check for additions
                foreach (string parameterSetName in value)
                {
                    // They want to add the parameter to a parameter set
                    if (!currentParameterSetNames.Contains(parameterSetName))
                    {
                        List<PsSpecCmdletParameterSetEntry> unremovedItems = new List<PsSpecCmdletParameterSetEntry>();

                        // See if we can undelete it
                        foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in removedParameterSetEntries)
                        {
                            if ((parameterSetEntry.ParameterId == this.Id) &&
                                (parameterSetEntry.ParameterSetName == parameterSetName))
                            {
                                unremovedItems.Add(parameterSetEntry);
                                parameterSetEntries.Add(parameterSetEntry);
                            }
                        }

                        // If we could undelete it, do so.
                        if (unremovedItems.Count > 0)
                        {
                            // Remove the undeleted items from the removed list
                            foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in unremovedItems)
                            {
                                if ((parameterSetEntry.ParameterId == this.Id) &&
                                    (parameterSetEntry.ParameterSetName == parameterSetName))
                                {
                                    removedParameterSetEntries.Remove(parameterSetEntry);
                                }
                            }
                        }
                        // Otherwise, create a new parameter set entry
                        else
                        {
                            PsSpecCmdletParameterSetEntry newEntry = new PsSpecCmdletParameterSetEntry();
                            newEntry.CmdletName = this.CmdletName;

                            if (this.HelpMessage != Constants.VariesByParameterSet)
                            {
                                newEntry.HelpMessage = this.HelpMessage;
                            }

                            if (this.HelpMessageBaseName != Constants.VariesByParameterSet)
                            {
                                newEntry.HelpMessageBaseName = this.HelpMessageBaseName;
                            }

                            if (this.HelpMessageResourceId != Constants.VariesByParameterSet)
                            {
                                newEntry.HelpMessageResourceId = this.HelpMessageResourceId;
                            }

                            newEntry.Id = -1;
                            newEntry.Mandatory = this.Mandatory.GetValueOrDefault(false);
                            newEntry.ParameterName = this.Name;
                            newEntry.ParameterSetName = parameterSetName;

                            int newPosition;
                            if (Int32.TryParse(this.Position, out newPosition))
                            {
                                newEntry.Position = newPosition;
                            }

                            newEntry.ProjectName = this.ProjectName;
                            newEntry.ValueFromPipeline = this.ValueFromPipeline.GetValueOrDefault(false);
                            newEntry.ValueFromPipelineByPropertyName = this.ValueFromPipelineByPropertyName.GetValueOrDefault(false);
                            newEntry.ValueFromRemainingArguments = this.ValueFromRemainingArguments.GetValueOrDefault(false);                            
                            this.parameterSetEntries.Add(newEntry);
                        }
                    }
                }

                // Actually remove the items that were not in the new list
                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in entriesToRemove)
                {
                    removedParameterSetEntries.Add(parameterSetEntry);
                    parameterSetEntries.Remove(parameterSetEntry);
                }

                // Check for duplicates
                Dictionary<string, PsSpecCmdletParameterSetEntry> entriesToKeep = new Dictionary<string, PsSpecCmdletParameterSetEntry>();

                foreach (PsSpecCmdletParameterSetEntry parameterSetEntry in parameterSetEntries)
                {
                    string ParameterKey = parameterSetEntry.ParameterName + parameterSetEntry.ParameterSetName + parameterSetEntry.Id.ToString();
                    //are there duplicates? If so then remove them.
                    if (!entriesToKeep.ContainsKey(ParameterKey)  )
                    {
                        entriesToKeep.Add(ParameterKey, parameterSetEntry);
                    }

                }

                this.parameterSetEntries.Clear();
                foreach (string key in entriesToKeep.Keys)
                {
                    this.parameterSetEntries.Add(entriesToKeep[key]);
                }                
            }
        }

        #region IComparable Members

        public int CompareTo(object obj)
        {
            PsSpecCmdletParameter comparisonParameter = obj as PsSpecCmdletParameter;
            if (comparisonParameter != null)
            {
                if (comparisonParameter.Equals(this.parameter))
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                throw new Exception("Comparison is not supported on these types.");
            }
        }

        #endregion
    }
}
